package com.flyxteam.commons.crypto;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.flyxteam.commons.base.util.Base64Util;

/**
 * <b>功能：</b>使用AES算法进行加密解密<br>
 * <b>说明：</b><br>
 * 1.使用{@link #genKey()}生成一个key.<br>
 * 2.利用生成的key,使用{@link #encrypt(byte[], byte[])}方法加密.<br>
 * 3.使用{@link #decrypt(byte[], byte[])}解密.<br>
 * 4.可以使用{@link #genKeyString()}生成字符串形式的key, 便于存储. 实质上是字节数组key进行了base64编码. 使用字节数组key性能更好一点点.<br>
 * <b>示例：</b><br>
    byte[] plan = new byte[]{1,2,3,4,5};//要加密的<br>
    byte[] key = AESUtil.genKey();//生成一个key<br>
    byte[] cipher = AESUtil.encrypt(plan, key);//加密<br>
    byte[] decrypt = AESUtil.decrypt(cipher, key);//解密<br>
 * <ul>
 * <li>2017-01-01 | ChrisX | + create</li>
 * </ul>
 * @author ChrisX
 */
public class AESUtil {
    
    private AESUtil() {}

	/** 算法名称 */
	private static final String KEY_ALGORITHM_AES = "AES";
	
	/** 算法方式*/
	private static final String CIPHER_ALGORITHM_AES = "AES/ECB/PKCS5Padding";
	
	//------------------------ 生成密钥

	/**
	 * <b>功能描述：</b>生成随机密钥,以base64编码.<br>
	 * {@link #genKey()}对结果进行base64.<br>
	 * @return 密钥的字节数组base64编码字符串
     */
    public static String genKeyString() {
    	byte[] key = genKey();
    	return Base64Util.encode(key);
    }
    
    /**
     * 生成随机密钥.<br>
     * @throws RuntimeException NoSuchAlgorithmException的包装
     * {@link #genKey(String)}.
     */
    public static byte[] genKey() {
    	return genKey(null);
    }
    
    /**
     * <b>功能描述：</b>生成随机密钥.<br>
     * @param seed 随机种子
     * @return 密钥的字节数组
     * @throws RuntimeException NoSuchAlgorithmException的包装
     */
    private static byte[] genKey(String seed) {
    	SecureRandom secureRandom = null;
    	if (seed != null) {
    		secureRandom = new SecureRandom(Base64Util.decode(seed));
    	} else {
    		secureRandom = new SecureRandom();
    	}
    	KeyGenerator kg;
    	try {
    		kg = KeyGenerator.getInstance(KEY_ALGORITHM_AES);
    	} catch (NoSuchAlgorithmException e) {
    		throw new RuntimeException(e);//应该不会出这个异常
    	}
    	kg.init(secureRandom);
    	SecretKey secretKey = kg.generateKey();
    	return secretKey.getEncoded();
    }
    
    //------------------------ 加密
    
	/**
	 * <b>功能描述：</b>加密，使用字符串秘钥.原理是对密钥base64.<br>
	 * {@link #encrypt(byte[], byte[])}.
	 * @param data 被加密对象
	 * @param key 密钥
	 */
	public static byte[] encrypt(byte[] data, String key) {
		return encrypt(data, Base64Util.decode(key));
	}

	/**
	 * <b>功能描述：</b>加密，使用二进制秘钥 .<br>
	 * @param data 被加密对象
	 * @param key 密钥
	 * @throws RuntimeException 封装了解密的各类异常, 这些应该都不会发生.
	 */
	public static byte[] encrypt(byte[] data, byte[] key) {
		SecretKeySpec sks = new SecretKeySpec(key, KEY_ALGORITHM_AES);
		Cipher cipher;
		try {
			cipher = Cipher.getInstance(CIPHER_ALGORITHM_AES);//创建密码器
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);//应该不会
		} catch (NoSuchPaddingException e) {
			throw new RuntimeException(e);//应该不会
		}
		try {
			cipher.init(Cipher.ENCRYPT_MODE, sks);// 初始化
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);//应该不会, 除非给一个不符合规范的key
		}
		try {
			return cipher.doFinal(data);
		} catch (IllegalBlockSizeException e) {
			throw new RuntimeException(e);//应该不会
		} catch (BadPaddingException e) {
			throw new RuntimeException(e);//应该不会
		}
	}

	//------------------------ 解密

	/**
	 * <b>功能描述：</b>解密，使用字符串秘钥.原理是对key进行base64<br>
	 * {@link #decrypt(byte[], byte[])}.
	 * @param data 密文
	 * @param key 密钥
	 */
	public static byte[] decrypt(byte[] data, String key) throws Exception {
		return decrypt(data, Base64Util.decode(key));
	}
    
	/**
	 * <b>功能描述：</b>解密，使用二进制秘钥 <br>
	 * @param data 密文
	 * @param key 密钥
	 * @throws RuntimeException 封装了解密的各类异常, 这些应该都不会发生.
	 */
	public static byte[] decrypt(byte[] data, byte[] key) {
		SecretKeySpec sks = new SecretKeySpec(key, KEY_ALGORITHM_AES);
		Cipher cipher;
		try {
			cipher = Cipher.getInstance(CIPHER_ALGORITHM_AES);// 创建密码器
		} catch (NoSuchAlgorithmException e) {
			throw new RuntimeException(e);//应该不会
		} catch (NoSuchPaddingException e) {
			throw new RuntimeException(e);//应该不会
		}
		try {
			cipher.init(Cipher.DECRYPT_MODE, sks);// 初始化
		} catch (InvalidKeyException e) {
			throw new RuntimeException(e);//应该不会, 除非给一个不符合规范的key
		}
		try {
			return cipher.doFinal(data);
		} catch (IllegalBlockSizeException e) {
			throw new RuntimeException(e);//应该不会
		} catch (BadPaddingException e) {
			throw new RuntimeException(e);//应该不会
		}
	}
	
}
